package com.zcily.demo.service.impl;

import com.zcily.demo.entity.vo.StorageObject;
import com.zcily.demo.prop.MinIOProperties;
import com.zcily.demo.service.MinIOService;
import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class MinIOServiceImpl implements MinIOService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinIOProperties minIOProperties;

    @Value("${spring.application.name}")
    private String applicationName;

    private static final List<String> SPECIAL_EXTENSION_NAMES = Arrays.asList("tar.xz", "tar.gz", "tar.bz2", "tar.Z");

    /**
     * 判断bucket是否存在，不存在则创建
     */
    public boolean existBucket(String bucketName) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                exists = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            exists = false;
        }
        return exists;
    }

    /**
     * 删除bucket
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 上传文件
     */
    public StorageObject upload(MultipartFile file) {
        RandomStringUtils.random(32, true, true);
        String storageName = applicationName + "/" + RandomStringUtils.random(32, true, true);
        String fileName = file.getOriginalFilename(); // 获取到上传的文件名
        boolean exist = false;
        for (String extensionName : SPECIAL_EXTENSION_NAMES) {
            if (fileName.endsWith("." + extensionName)) {
                storageName += "." + extensionName;
                exist = true;
                break;
            }
        }
        if (!exist) {
            int pos = fileName.lastIndexOf(".");
            if (pos > -1 && pos < fileName.length() - 1) {
                storageName += fileName.substring(pos);
            }
        }

        StorageObject storageObject = new StorageObject(fileName, storageName, file.getSize(), file.getContentType());
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            // 使用putObject上传一个文件到存储桶中。
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minIOProperties.getBucketName())
                    .object(storageObject.getStorageName())
                    .stream(inputStream, storageObject.getSize(), -1)
                    .contentType(storageObject.getContentType())
                    .build()
            );
        } catch (Exception e) {
            log.error("上传文件失败", e);
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception exception) {
                    log.error("上传文件后，关闭InputStream时发生错误", exception);
                }
            }
        }

        return storageObject;
    }

    /**
     * 获取文件访问地址（有过期时间）
     */
    public String getExpireFileUrl(String fileName, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(minIOProperties.getBucketName())
                    .object(fileName)
                    .expiry(time, timeUnit).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取文件访问地址
     */
    public String getFileUrl(String fileName) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(minIOProperties.getBucketName())
                    .object(fileName)
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 下载文件
     */
    public void download(HttpServletResponse response, String fileName) {
        InputStream inputStream = null;
        try {
            // 获取对象信息
            StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(minIOProperties.getBucketName()).object(fileName).build());
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            // 文件下载
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOProperties.getBucketName()).object(fileName).build());
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            log.error("下载文件失败", e);
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception exception) {
                    log.error("下载文件后，关闭InputStream时发生错误", exception);
                }
            }
        }
    }

    /**
     * 删除文件
     */
    public void delete(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(minIOProperties.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

